/*******************************************************************************
* @file    
* @brief   虚拟串口
*******************************************************************************/
#include "bsp.h"
#include "bsp_api.h"
#include "pincfg.h"
#include "stm32f4xx_ll_usart.h"
#include "os_obj.h"  
#include "dataproc.h" 
#include "com_obj.h" 
#include "bsp_cpu_it.h"
#include "str_ext.h"

/*******************************************************************************
* @brief   基本接口
*******************************************************************************/
#define com_dir_RxEnable()
#define com_dir_TxEnable()

static void com_hal_init(uint8_t comMode, uint32_t ulBaudRate, com_parity_type eParity);
static void com_rx_enbale(uint8_t xEnable);
static void com_tx_enbale(uint8_t xEnable);
static void com_timer_handle(void);
static void com_fun_init(uint8_t comMode);
static void	com_hal_send(uint8_t dat);
static	int16_t com_hal_read(void);

/*******************************************************************************
* @brief   本地变量
*******************************************************************************/
static uint8_t b_dir_ctr_req;
static const com_fun_type com_fun;

/*******************************************************************************
* @brief   虚拟接口
*******************************************************************************/
#ifndef ComObj

USART_TypeDef * UartSim;

/*******************************************************************************
* @brief   接口功能
*******************************************************************************/
#define COM_RX_EN                   1
#define COM_RX_CALL_BACK_EN         0
#define MB_RS_485_MODE_EN           0
#define MB_RS_485_HALDIR_EN         0

#define comPORT						UartSim
#define comIsr						COM_UART_Sim_IRQHandler
//#define RCU_USART					RCC_APB2PERIPH_UART8
//#define RCU_USARTRST				RCC_APB2PERIPH_UART8
#define COM_IRQn					USART1_IRQn
#define ComObj						com.obj01

static uint8_t com_tx_buff[2];
static uint8_t com_rx_buff[2];

/*******************************************************************************
* @brief   FIFO 尺寸
*******************************************************************************/
#define COM_PORT_FIFO_SIZE      1

/*******************************************************************************
* @brief   PIN CFG.
*
* @ Pass/ Fail criteria: none
*******************************************************************************/
static void com_pin_cfg(void)
{
}

/*******************************************************************************
* @brief   	com port init .
*
* @ Pass/ Fail criteria: none
*******************************************************************************/
void com_sim_Init(uint8_t comMode, uint32_t ulBaudRate, com_parity_type eParity)
{
	com_hal_init(comMode, ulBaudRate, eParity);
}

#endif

/*******************************************************************************
* @brief   串口底层操作
*******************************************************************************/
#define UART_SEND_DAT(dat)  comPORT->DR=dat
#define UART_RCV_DAT()		comPORT->DR
#define UART_RX_NE_ST()		((comPORT->SR & USART_SR_RXNE) != 0)	//rx not empty
#define UART_TX_NF_ST()     (0 != (comPORT->SR & USART_SR_TXE))		//tx not full
#define UART_RX_INT_EN()    comPORT->CR1 |= (USART_CR1_UE | USART_CR1_RE | USART_CR1_RXNEIE)   //开启RX中断
#define UART_RX_INT_DIS()   comPORT->CR1 &= ~(USART_CR1_RXNEIE | USART_CR1_RE)
#define UART_RX_ERR_ST()    (comPORT->SR & (USART_SR_PE | USART_SR_FE))
#define UART_RX_ERR_CLR()   //先读 sts 再读 dt自动清除
#define UART_TX_INT_EN()    comPORT->CR1 |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_TXEIE) 
#define UART_TX_INT_DIS()   comPORT->CR1 &= ~(USART_CR1_TXEIE)
#define UART_TX_EN()   		comPORT->CR1 |= (USART_CR1_UE | USART_CR1_TE) 
#define UART_TX_TC_ST()     ((comPORT->SR & USART_SR_TC) != 0)

static void UART_RX_DAT_CLR()
{
  volatile uint8_t u8Temp;
  while (UART_RX_NE_ST())
  {
    u8Temp = UART_RCV_DAT();
  }
}


#include "com_fun_ptr.h"

/*****************************************************************************
* @brief   com object init.
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void com_fun_init(uint8_t comMode)
{
    ComObj.St.bits.run_en = 1;
    ComObj.master_wait_time_sv = 200;
    ComObj.rcv_end_keep_sv = 3;
    ComObj.rx_buff = com_rx_buff;
    ComObj.tx_buff = com_tx_buff;
    ComObj.rx_buff_size = sizeof(com_rx_buff);
    ComObj.tx_buff_size = sizeof(com_tx_buff);
    ComObj.b_auto_send = 1;
	
    ComObj.fun = &com_fun;
    
    ComObj.b_master_mode = comMode;
}

/*****************************************************************************
* @brief   	com modle init .
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void com_hal_init(uint8_t comMode, uint32_t ulBaudRate, com_parity_type eParity)
{
    uint32_t us10TimerSv;
    uint32_t ClocksPer2ms;
    LL_USART_InitTypeDef cfg;
    
    com_pin_cfg();
    
    set_u8(&ComObj, 0, sizeof(ComObj));
    
    cfg.BaudRate = ulBaudRate;
    cfg.TransferDirection = LL_USART_DIRECTION_TX_RX;
    cfg.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
    cfg.OverSampling = LL_USART_OVERSAMPLING_16;
    
    if (eParity == COM_PAR_ODD)
    {
			cfg.Parity = LL_USART_PARITY_ODD;
			cfg.StopBits = LL_USART_STOPBITS_1;
			cfg.DataWidth = LL_USART_DATAWIDTH_9B;
    }
    else if (eParity == COM_PAR_EVEN)
    {
			cfg.Parity = LL_USART_PARITY_EVEN;
			cfg.StopBits = LL_USART_STOPBITS_1;
			cfg.DataWidth = LL_USART_DATAWIDTH_9B;
    }
    else if (eParity == COM_PAR_NONE_2S)
    {
			cfg.Parity = LL_USART_PARITY_NONE;
			cfg.StopBits = LL_USART_STOPBITS_2;
			cfg.DataWidth = LL_USART_DATAWIDTH_8B;
    }
    else
    {
			cfg.Parity = LL_USART_PARITY_NONE;
			cfg.StopBits = LL_USART_STOPBITS_1;
			cfg.DataWidth = LL_USART_DATAWIDTH_8B;
    }

    //11*3.5*(1E6)/ba/50 = 77*1E4/ba
    if (ulBaudRate > 19200)
    {
        ClocksPer2ms = 1800 * ulBaudRate / 1000000 + 1;
        
        if (ClocksPer2ms < 41)
            ClocksPer2ms = 41;
        else if (ClocksPer2ms > 255)
            ClocksPer2ms = 255;
        //TOI 时间设置
    }
    else
    {
        // Time-out Register ,TX-DLY=0, TX-TIMEOUT=40
        //TOI 时间设置
    }

	// 报文通知延时
    us10TimerSv = com_timer_cal(ulBaudRate, 11*5, 300, 20000);
    ComObj.rcv_end_time_sv = us10TimerSv/100;
    
    if (comMode > 0)
    {
        ComObj.rcv_end_time_sv ++;
    }
	
    LL_USART_Init(comPORT, &cfg);

    LL_USART_Enable(comPORT);
	
    nvic_irq_set(COM_IRQn, 0x0C, 1);
    
    com_fun_init(comMode);
    
    com_rx_enbale(ENABLE);
}

/*****************************************************************************
* @brief   com end timer.
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void com_dir_handle()
{
    if (b_dir_ctr_req)
    {    
#if ((MB_RS_485_MODE_EN > 0) && (MB_RS_485_HALDIR_EN == 0))
		// RS485模式且软件控制方向时，发送结束切换为接收模式
        if ( UART_TX_TC_ST())
        {
            b_dir_ctr_req = 0;
            ComObj.snd_st = COM_TX_IDLE;        //发送结束
            com_tx_enbale(DISABLE);
            if (ComObj.b_master_mode)           //主机进入接收等待状态
            {
                ComObj.rcv_st = COM_RX_WAIT;
                com_rx_enbale(ENABLE);
            }
        }
#endif 
    }         
}

/*****************************************************************************
* @brief   com end timer.
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void com_timer_handle(void)
{
    com_dir_handle();

	com_timer_run(&ComObj);
}

/*****************************************************************************
* @brief   循环模式中断处理
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void com_isr_cycle_mode(void) 
{
    volatile uint32_t IntSt;
     
    IntSt = comPORT->SR;
    ComObj.master_wait_time_cnt = 0;
    
    #if COM_RX_EN > 0
    if(IntSt & USART_SR_RXNE)
    {
        uint16_t ReadCnt;
        
        // FIFO 中断
        ComObj.St.bits.port_st = 1;

        // 接收状态
        ComObj.rcv_st = COM_RX_RCV; //指示正在接收数据
        ComObj.b_timer_on = 1;
        ComObj.rcv_end_time_cnt = 0;
		
        // 读数据
        ReadCnt = COM_PORT_FIFO_SIZE; //FIFO read
        while (ReadCnt--)
        {
            uint8_t datr;
            datr = UART_RCV_DAT();
			
            com_rx_rcv(&ComObj, datr);
            
            #if COM_RX_CALL_BACK_EN > 0
            if(ComObj.rx_call_back)
            {
                ComObj.rx_call_back(datr);
            }
            #endif
        }
    }
    else if(IntSt & USART_SR_TXE)
    #else
    if(IntSt & USART_SR_TXE)
    #endif
    //发送缓冲空触发
    {
        if (ComObj.index_tx_send == ComObj.index_tx_fill)
        {
            //发送已结束 //关闭中断
            com_tx_enbale(DISABLE);
			
			if(ComObj.b_master_mode)
			{
#if ((MB_RS_485_MODE_EN > 0) && (MB_RS_485_HALDIR_EN == 0))
				b_dir_ctr_req = 1;
#else
				ComObj.rcv_st = COM_RX_WAIT;
#endif
			}
        }
        else
        {
            //发送空中断
            while (UART_TX_NF_ST()) //缓冲未满
            {
                if (ComObj.index_tx_send != ComObj.index_tx_fill) //写缓冲区结束
                {
                    //继续发送
                    UART_SEND_DAT(ComObj.tx_buff[ComObj.index_tx_send++]);
                    
                    if(ComObj.index_tx_send >= ComObj.tx_buff_size)
                    {
                        ComObj.index_tx_send = 0;
                    }
                }
                else
                {
                    //发送结束
                    break;
                }
            }
        }
    }
    else
    {
        //未知错误，应重新初始化串口设备
		//ComObj.St.bits.err_hal = 1;
		//nvic_irq_set(COM_IRQn,0x0F,0);
    }
}

/*****************************************************************************
* @brief   中断入口
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
void comIsr(void) 
{
  com_isr_cycle_mode();
}

/*****************************************************************************
* @brief   Uart Rx En or Dis.
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void com_rx_enbale(uint8_t xRxEnable)
{
    volatile uint8_t u8Temp;

    if (xRxEnable)
    {
        #if COM_RX_EN > 0
        //清空接收缓冲
        UART_RX_DAT_CLR();
        
        //清除帧错误和奇偶校验错误
        UART_RX_ERR_CLR();
        
        //开始接收
        UART_RX_INT_EN();
        com_dir_RxEnable();
        #else
        UART_RX_INT_DIS();
        #endif
    }
    else
    {
        UART_RX_INT_DIS();
    }
}

/*****************************************************************************
* @brief   Uart Tx En or Dis.
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void com_tx_enbale(uint8_t xEnable)
{
    if (xEnable)
    {
        //准备发送
        com_dir_TxEnable();
        
        ComObj.snd_st = COM_TX_XMIT;
		
        //Interrupt Enable Register 打开发送空中断
        UART_TX_INT_EN();
    }
    else
    {
        UART_TX_INT_DIS();
        com_dir_RxEnable();
        ComObj.snd_st = COM_TX_IDLE;
    }
}


static void	com_hal_send(uint8_t dat)
{
	UART_TX_EN();
	
	UART_SEND_DAT(dat);
	
	while (!UART_TX_NF_ST())
	{
	}
}

static	int16_t com_hal_read(void)
{
	return -1;
}
